


<!DOCTYPE html>
<html id="htmlId">
<head>
  <title>Coverage Report > Level</title>
  <style type="text/css">
    @import "../../css/coverage.css";
    @import "../../css/idea.min.css";
  </style>
  <script type="text/javascript" src="../../js/highlight.min.js"></script>
  <script type="text/javascript" src="../../js/highlightjs-line-numbers.min.js"></script>
</head>

<body>
<div class="content">
<div class="breadCrumbs">
Current scope:     <a href="../../index.html">all classes</a>
    <span class="separator">|</span>
    <a href="../index.html">nl.tudelft.jpacman.level</a>
</div>

<h1>Coverage Summary for Class: Level (nl.tudelft.jpacman.level)</h1>

<table class="coverageStats">

<tr>
  <th class="name">Class</th>
<th class="coverageStat 
">
  Method, %
</th>
<th class="coverageStat 
">
  Line, %
</th>
</tr>
<tr>
  <td class="name">Level</td>
<td class="coverageStat">
  <span class="percent">
    0%
  </span>
  <span class="absValue">
    (0/15)
  </span>
</td>
<td class="coverageStat">
  <span class="percent">
    0%
  </span>
  <span class="absValue">
    (0/93)
  </span>
</td>
</tr>
  <tr>
    <td class="name">Level$LevelObserver</td>
  </tr>
  <tr>
    <td class="name">Level$NpcMoveTask</td>
<td class="coverageStat">
  <span class="percent">
    0%
  </span>
  <span class="absValue">
    (0/2)
  </span>
</td>
<td class="coverageStat">
  <span class="percent">
    0%
  </span>
  <span class="absValue">
    (0/8)
  </span>
</td>
  </tr>
<tr>
  <td class="name"><strong>Total</strong></td>
<td class="coverageStat">
  <span class="percent">
    0%
  </span>
  <span class="absValue">
    (0/17)
  </span>
</td>
<td class="coverageStat">
  <span class="percent">
    0%
  </span>
  <span class="absValue">
    (0/101)
  </span>
</td>
</tr>
</table>

<br/>
<br/>


<pre>
<code class="sourceCode" id="sourceCode">&nbsp;package nl.tudelft.jpacman.level;
&nbsp;
&nbsp;import java.util.ArrayList;
&nbsp;import java.util.HashMap;
&nbsp;import java.util.HashSet;
&nbsp;import java.util.List;
&nbsp;import java.util.Map;
&nbsp;import java.util.Map.Entry;
&nbsp;import java.util.Set;
&nbsp;import java.util.concurrent.Executors;
&nbsp;import java.util.concurrent.ScheduledExecutorService;
&nbsp;import java.util.concurrent.TimeUnit;
&nbsp;
&nbsp;import nl.tudelft.jpacman.board.Board;
&nbsp;import nl.tudelft.jpacman.board.Direction;
&nbsp;import nl.tudelft.jpacman.board.Square;
&nbsp;import nl.tudelft.jpacman.board.Unit;
&nbsp;import nl.tudelft.jpacman.npc.Ghost;
&nbsp;
&nbsp;/**
&nbsp; * A level of Pac-Man. A level consists of the board with the players and the
&nbsp; * AIs on it.
&nbsp; *
&nbsp; * @author Jeroen Roosen 
&nbsp; */
<b class="nc">&nbsp;@SuppressWarnings(&quot;PMD.TooManyMethods&quot;)</b>
&nbsp;public class Level {
&nbsp;
&nbsp;    /**
&nbsp;     * The board of this level.
&nbsp;     */
&nbsp;    private final Board board;
&nbsp;
&nbsp;    /**
&nbsp;     * The lock that ensures moves are executed sequential.
&nbsp;     */
<b class="nc">&nbsp;    private final Object moveLock = new Object();</b>
&nbsp;
&nbsp;    /**
&nbsp;     * The lock that ensures starting and stopping can&#39;t interfere with each
&nbsp;     * other.
&nbsp;     */
<b class="nc">&nbsp;    private final Object startStopLock = new Object();</b>
&nbsp;
&nbsp;    /**
&nbsp;     * The NPCs of this level and, if they are running, their schedules.
&nbsp;     */
&nbsp;    private final Map&lt;Ghost, ScheduledExecutorService&gt; npcs;
&nbsp;
&nbsp;    /**
&nbsp;     * &lt;code&gt;true&lt;/code&gt; iff this level is currently in progress, i.e. players
&nbsp;     * and NPCs can move.
&nbsp;     */
&nbsp;    private boolean inProgress;
&nbsp;
&nbsp;    /**
&nbsp;     * The squares from which players can start this game.
&nbsp;     */
&nbsp;    private final List&lt;Square&gt; startSquares;
&nbsp;
&nbsp;    /**
&nbsp;     * The start current selected starting square.
&nbsp;     */
&nbsp;    private int startSquareIndex;
&nbsp;
&nbsp;    /**
&nbsp;     * The players on this level.
&nbsp;     */
&nbsp;    private final List&lt;Player&gt; players;
&nbsp;
&nbsp;    /**
&nbsp;     * The table of possible collisions between units.
&nbsp;     */
&nbsp;    private final CollisionMap collisions;
&nbsp;
&nbsp;    /**
&nbsp;     * The objects observing this level.
&nbsp;     */
&nbsp;    private final Set&lt;LevelObserver&gt; observers;
&nbsp;
&nbsp;    /**
&nbsp;     * Creates a new level for the board.
&nbsp;     *
&nbsp;     * @param board
&nbsp;     *            The board for the level.
&nbsp;     * @param ghosts
&nbsp;     *            The ghosts on the board.
&nbsp;     * @param startPositions
&nbsp;     *            The squares on which players start on this board.
&nbsp;     * @param collisionMap
&nbsp;     *            The collection of collisions that should be handled.
&nbsp;     */
&nbsp;    public Level(Board board, List&lt;Ghost&gt; ghosts, List&lt;Square&gt; startPositions,
<b class="nc">&nbsp;                 CollisionMap collisionMap) {</b>
<b class="nc">&nbsp;        assert board != null;</b>
<b class="nc">&nbsp;        assert ghosts != null;</b>
<b class="nc">&nbsp;        assert startPositions != null;</b>
&nbsp;
<b class="nc">&nbsp;        this.board = board;</b>
<b class="nc">&nbsp;        this.inProgress = false;</b>
<b class="nc">&nbsp;        this.npcs = new HashMap&lt;&gt;();</b>
<b class="nc">&nbsp;        for (Ghost ghost : ghosts) {</b>
<b class="nc">&nbsp;            npcs.put(ghost, null);</b>
<b class="nc">&nbsp;        }</b>
<b class="nc">&nbsp;        this.startSquares = startPositions;</b>
<b class="nc">&nbsp;        this.startSquareIndex = 0;</b>
<b class="nc">&nbsp;        this.players = new ArrayList&lt;&gt;();</b>
<b class="nc">&nbsp;        this.collisions = collisionMap;</b>
<b class="nc">&nbsp;        this.observers = new HashSet&lt;&gt;();</b>
&nbsp;    }
&nbsp;
&nbsp;    /**
&nbsp;     * Adds an observer that will be notified when the level is won or lost.
&nbsp;     *
&nbsp;     * @param observer
&nbsp;     *            The observer that will be notified.
&nbsp;     */
&nbsp;    public void addObserver(LevelObserver observer) {
<b class="nc">&nbsp;        observers.add(observer);</b>
&nbsp;    }
&nbsp;
&nbsp;    /**
&nbsp;     * Removes an observer if it was listed.
&nbsp;     *
&nbsp;     * @param observer
&nbsp;     *            The observer to be removed.
&nbsp;     */
&nbsp;    public void removeObserver(LevelObserver observer) {
<b class="nc">&nbsp;        observers.remove(observer);</b>
&nbsp;    }
&nbsp;
&nbsp;    /**
&nbsp;     * Registers a player on this level, assigning him to a starting position. A
&nbsp;     * player can only be registered once, registering a player again will have
&nbsp;     * no effect.
&nbsp;     *
&nbsp;     * @param player
&nbsp;     *            The player to register.
&nbsp;     */
&nbsp;    public void registerPlayer(Player player) {
<b class="nc">&nbsp;        assert player != null;</b>
<b class="nc">&nbsp;        assert !startSquares.isEmpty();</b>
&nbsp;
<b class="nc">&nbsp;        if (players.contains(player)) {</b>
&nbsp;            return;
&nbsp;        }
<b class="nc">&nbsp;        players.add(player);</b>
<b class="nc">&nbsp;        Square square = startSquares.get(startSquareIndex);</b>
<b class="nc">&nbsp;        player.occupy(square);</b>
<b class="nc">&nbsp;        startSquareIndex++;</b>
<b class="nc">&nbsp;        startSquareIndex %= startSquares.size();</b>
&nbsp;    }
&nbsp;
&nbsp;    /**
&nbsp;     * Returns the board of this level.
&nbsp;     *
&nbsp;     * @return The board of this level.
&nbsp;     */
&nbsp;    public Board getBoard() {
<b class="nc">&nbsp;        return board;</b>
&nbsp;    }
&nbsp;
&nbsp;    /**
&nbsp;     * Moves the unit into the given direction if possible and handles all
&nbsp;     * collisions.
&nbsp;     *
&nbsp;     * @param unit
&nbsp;     *            The unit to move.
&nbsp;     * @param direction
&nbsp;     *            The direction to move the unit in.
&nbsp;     */
&nbsp;    public void move(Unit unit, Direction direction) {
<b class="nc">&nbsp;        assert unit != null;</b>
<b class="nc">&nbsp;        assert direction != null;</b>
<b class="nc">&nbsp;        assert unit.hasSquare();</b>
&nbsp;
<b class="nc">&nbsp;        if (!isInProgress()) {</b>
&nbsp;            return;
&nbsp;        }
&nbsp;
<b class="nc">&nbsp;        synchronized (moveLock) {</b>
<b class="nc">&nbsp;            unit.setDirection(direction);</b>
<b class="nc">&nbsp;            Square location = unit.getSquare();</b>
<b class="nc">&nbsp;            Square destination = location.getSquareAt(direction);</b>
&nbsp;
<b class="nc">&nbsp;            if (destination.isAccessibleTo(unit)) {</b>
<b class="nc">&nbsp;                List&lt;Unit&gt; occupants = destination.getOccupants();</b>
<b class="nc">&nbsp;                unit.occupy(destination);</b>
<b class="nc">&nbsp;                for (Unit occupant : occupants) {</b>
<b class="nc">&nbsp;                    collisions.collide(unit, occupant);</b>
<b class="nc">&nbsp;                }</b>
&nbsp;            }
<b class="nc">&nbsp;            updateObservers();</b>
<b class="nc">&nbsp;        }</b>
&nbsp;    }
&nbsp;
&nbsp;    /**
&nbsp;     * Starts or resumes this level, allowing movement and (re)starting the
&nbsp;     * NPCs.
&nbsp;     */
&nbsp;    public void start() {
<b class="nc">&nbsp;        synchronized (startStopLock) {</b>
<b class="nc">&nbsp;            if (isInProgress()) {</b>
<b class="nc">&nbsp;                return;</b>
&nbsp;            }
<b class="nc">&nbsp;            startNPCs();</b>
<b class="nc">&nbsp;            inProgress = true;</b>
<b class="nc">&nbsp;            updateObservers();</b>
<b class="nc">&nbsp;        }</b>
&nbsp;    }
&nbsp;
&nbsp;    /**
&nbsp;     * Stops or pauses this level, no longer allowing any movement on the board
&nbsp;     * and stopping all NPCs.
&nbsp;     */
&nbsp;    public void stop() {
<b class="nc">&nbsp;        synchronized (startStopLock) {</b>
<b class="nc">&nbsp;            if (!isInProgress()) {</b>
<b class="nc">&nbsp;                return;</b>
&nbsp;            }
<b class="nc">&nbsp;            stopNPCs();</b>
<b class="nc">&nbsp;            inProgress = false;</b>
<b class="nc">&nbsp;        }</b>
&nbsp;    }
&nbsp;
&nbsp;    /**
&nbsp;     * Starts all NPC movement scheduling.
&nbsp;     */
&nbsp;    private void startNPCs() {
<b class="nc">&nbsp;        for (final Ghost npc : npcs.keySet()) {</b>
<b class="nc">&nbsp;            ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();</b>
&nbsp;
<b class="nc">&nbsp;            service.schedule(new NpcMoveTask(service, npc),</b>
<b class="nc">&nbsp;                npc.getInterval() / 2, TimeUnit.MILLISECONDS);</b>
&nbsp;
<b class="nc">&nbsp;            npcs.put(npc, service);</b>
<b class="nc">&nbsp;        }</b>
&nbsp;    }
&nbsp;
&nbsp;    /**
&nbsp;     * Stops all NPC movement scheduling and interrupts any movements being
&nbsp;     * executed.
&nbsp;     */
&nbsp;    private void stopNPCs() {
<b class="nc">&nbsp;        for (Entry&lt;Ghost, ScheduledExecutorService&gt; entry : npcs.entrySet()) {</b>
<b class="nc">&nbsp;            ScheduledExecutorService schedule = entry.getValue();</b>
<b class="nc">&nbsp;            assert schedule != null;</b>
<b class="nc">&nbsp;            schedule.shutdownNow();</b>
<b class="nc">&nbsp;        }</b>
&nbsp;    }
&nbsp;
&nbsp;    /**
&nbsp;     * Returns whether this level is in progress, i.e. whether moves can be made
&nbsp;     * on the board.
&nbsp;     *
&nbsp;     * @return &lt;code&gt;true&lt;/code&gt; iff this level is in progress.
&nbsp;     */
&nbsp;    public boolean isInProgress() {
<b class="nc">&nbsp;        return inProgress;</b>
&nbsp;    }
&nbsp;
&nbsp;    /**
&nbsp;     * Updates the observers about the state of this level.
&nbsp;     */
&nbsp;    private void updateObservers() {
<b class="nc">&nbsp;        if (!isAnyPlayerAlive()) {</b>
<b class="nc">&nbsp;            for (LevelObserver observer : observers) {</b>
<b class="nc">&nbsp;                observer.levelLost();</b>
<b class="nc">&nbsp;            }</b>
&nbsp;        }
<b class="nc">&nbsp;        if (remainingPellets() == 0) {</b>
<b class="nc">&nbsp;            for (LevelObserver observer : observers) {</b>
<b class="nc">&nbsp;                observer.levelWon();</b>
<b class="nc">&nbsp;            }</b>
&nbsp;        }
&nbsp;    }
&nbsp;
&nbsp;    /**
&nbsp;     * Returns &lt;code&gt;true&lt;/code&gt; iff at least one of the players in this level
&nbsp;     * is alive.
&nbsp;     *
&nbsp;     * @return &lt;code&gt;true&lt;/code&gt; if at least one of the registered players is
&nbsp;     *         alive.
&nbsp;     */
&nbsp;    public boolean isAnyPlayerAlive() {
<b class="nc">&nbsp;        for (Player player : players) {</b>
<b class="nc">&nbsp;            if (player.isAlive()) {</b>
<b class="nc">&nbsp;                return true;</b>
&nbsp;            }
<b class="nc">&nbsp;        }</b>
<b class="nc">&nbsp;        return false;</b>
&nbsp;    }
&nbsp;
&nbsp;    /**
&nbsp;     * Counts the pellets remaining on the board.
&nbsp;     *
&nbsp;     * @return The amount of pellets remaining on the board.
&nbsp;     */
&nbsp;    public int remainingPellets() {
<b class="nc">&nbsp;        Board board = getBoard();</b>
<b class="nc">&nbsp;        int pellets = 0;</b>
<b class="nc">&nbsp;        for (int x = 0; x &lt; board.getWidth(); x++) {</b>
<b class="nc">&nbsp;            for (int y = 0; y &lt; board.getHeight(); y++) {</b>
<b class="nc">&nbsp;                for (Unit unit : board.squareAt(x, y).getOccupants()) {</b>
<b class="nc">&nbsp;                    if (unit instanceof Pellet) {</b>
<b class="nc">&nbsp;                        pellets++;</b>
&nbsp;                    }
<b class="nc">&nbsp;                }</b>
&nbsp;            }
&nbsp;        }
<b class="nc">&nbsp;        assert pellets &gt;= 0;</b>
<b class="nc">&nbsp;        return pellets;</b>
&nbsp;    }
&nbsp;
&nbsp;    /**
&nbsp;     * A task that moves an NPC and reschedules itself after it finished.
&nbsp;     *
&nbsp;     * @author Jeroen Roosen
&nbsp;     */
&nbsp;    private final class NpcMoveTask implements Runnable {
&nbsp;
&nbsp;        /**
&nbsp;         * The service executing the task.
&nbsp;         */
&nbsp;        private final ScheduledExecutorService service;
&nbsp;
&nbsp;        /**
&nbsp;         * The NPC to move.
&nbsp;         */
&nbsp;        private final Ghost npc;
&nbsp;
&nbsp;        /**
&nbsp;         * Creates a new task.
&nbsp;         *
&nbsp;         * @param service
&nbsp;         *            The service that executes the task.
&nbsp;         * @param npc
&nbsp;         *            The NPC to move.
&nbsp;         */
<b class="nc">&nbsp;        NpcMoveTask(ScheduledExecutorService service, Ghost npc) {</b>
<b class="nc">&nbsp;            this.service = service;</b>
<b class="nc">&nbsp;            this.npc = npc;</b>
&nbsp;        }
&nbsp;
&nbsp;        @Override
&nbsp;        public void run() {
<b class="nc">&nbsp;            Direction nextMove = npc.nextMove();</b>
<b class="nc">&nbsp;            if (nextMove != null) {</b>
<b class="nc">&nbsp;                move(npc, nextMove);</b>
&nbsp;            }
<b class="nc">&nbsp;            long interval = npc.getInterval();</b>
<b class="nc">&nbsp;            service.schedule(this, interval, TimeUnit.MILLISECONDS);</b>
&nbsp;        }
&nbsp;    }
&nbsp;
&nbsp;    /**
&nbsp;     * An observer that will be notified when the level is won or lost.
&nbsp;     *
&nbsp;     * @author Jeroen Roosen
&nbsp;     */
&nbsp;    public interface LevelObserver {
&nbsp;
&nbsp;        /**
&nbsp;         * The level has been won. Typically the level should be stopped when
&nbsp;         * this event is received.
&nbsp;         */
&nbsp;        void levelWon();
&nbsp;
&nbsp;        /**
&nbsp;         * The level has been lost. Typically the level should be stopped when
&nbsp;         * this event is received.
&nbsp;         */
&nbsp;        void levelLost();
&nbsp;    }
&nbsp;}
</code>
</pre>
</div>

<script type="text/javascript">
(function() {
    var msie = false, msie9 = false;
    /*@cc_on
      msie = true;
      @if (@_jscript_version >= 9)
        msie9 = true;
      @end
    @*/

    if (!msie || msie && msie9) {
      hljs.highlightAll()
      hljs.initLineNumbersOnLoad();
    }
})();
</script>

<div class="footer">
    
    <div style="float:right;">generated on 2023-04-11 19:58</div>
</div>
</body>
</html>
